﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using Microsoft.Practices.Prism.Mvvm;
using Microsoft.Practices.Prism.ViewModel;

namespace Infrastucture
{
    public abstract class DomainObject : BindableBase, INotifyDataErrorInfo, IEditableObject
    {
 
        public DomainObject()
        {
            // todo: need to add errors container
        }

        #region Enumeration...
        // enumeration
        protected void UpdateEnumeration<T>(string targetName, string value)
        {
           // need to get properties and find the one that has this name
            var prop = this.GetType().GetProperty(targetName);
            
            if (prop != null)
            {
                //may need to use a try catch to get an enum back
                try
                {
                    var val = Enum.Parse(typeof(T), value, true);
                    prop.SetValue(this, val);
                }
                catch (Exception ex)
                {

                    throw ex;
                }
            }
        }
        protected void UpdateEnumerationString<T>(string targetName, object value)
        {
            var prop = this.GetType().GetProperty(targetName);

            if (prop != null)
            {
                prop.SetValue(this, value.ToString());
            }
        }
        #endregion Enumeration

        #region Auto Saving...
        // for data grid saving and maybe auto saving single records
        // thin i need to move this part back to domain object
        public delegate void ItemEndEditEventHandler(IEditableObject sender);
        public event ItemEndEditEventHandler ItemEndEdit;

        public void BeginEdit() { }
        public void CancelEdit() { }
        public void EndEdit()
        {
            if (ItemEndEdit != null)
            {
                ItemEndEdit(this);
            }
        }
        #endregion Auto Saving

        #region Validation...
        // fields
        private object _validationLock = new object();
        private ErrorsContainer<string> _errorsContainer;
        private ConcurrentDictionary<string, List<string>> _errors = new ConcurrentDictionary<string, List<string>>();

        // events
        public event EventHandler<DataErrorsChangedEventArgs> ErrorsChanged;


        public IEnumerable GetErrors(string propertyName)
        {
            // may change to async method
            return this.ErrorsContainer.GetErrors(propertyName);
        }


        // properties
        [DoNotConvert]
        public bool HasErrors
        {
            get { return this.ErrorsContainer.HasErrors; }
        }
        [DoNotConvert]
        public ErrorsContainer<string> ErrorsContainer
        {
            get
            {
                if (this._errorsContainer == null)
                    this._errorsContainer = new ErrorsContainer<string>(this.RaiseErrorsChanged);

                return this._errorsContainer;
            }
        }


        // event handling
        protected virtual void OnErrorsChanged(DataErrorsChangedEventArgs e)
        {
            var handler = this.ErrorsChanged;
            if (handler != null)
                handler(this, e);
        }


        // Methods
        protected void RaiseErrorsChanged([CallerMemberName]string propertyName = "")
        {
            this.OnErrorsChanged(new DataErrorsChangedEventArgs(propertyName));
        }

        protected void ValidateProperty(object value, [CallerMemberName] string propertyName = "")
        {
            if (String.IsNullOrEmpty(propertyName))
                throw new ArgumentNullException("propertyName");

            ValidateProperty(new ValidationContext(this, null, null) { MemberName = propertyName }, value);
        }
        protected void ValidateProperty(ValidationContext context, object value)
        {
            if (context == null)
                throw new ArgumentNullException("context");

            var results = new List<ValidationResult>();

            Validator.TryValidateProperty(value, context, results);
            var r = results.Select(x => x.ErrorMessage).Distinct().ToList();

            this.ErrorsContainer.SetErrors(context.MemberName, r);
        }
        #endregion Validation
    }
}
